#include <linux/int.h>
#include <linux/irq.h>

struct gic_chip_data gic_data;

// disable irq
static void gic_mask_irq(struct irq_data *d)
{
	u32 hwirq = d->hwirq;

	printf("this is %s(): %d\r\n", __func__, __LINE__);
  	gic_data.gic->D_ICENABLER[((uint32_t)(int32_t)hwirq) >> 5] = (uint32_t)(1UL << (((uint32_t)(int32_t)hwirq) & 0x1FUL));
}

// enable irq
static void gic_unmask_irq(struct irq_data *d)
{
	u32 hwirq = d->hwirq;

	printf("this is %s(): %d\r\n", __func__, __LINE__);
  	gic_data.gic->D_ISENABLER[((uint32_t)(int32_t)hwirq) >> 5] = (uint32_t)(1UL << (((uint32_t)(int32_t)hwirq) & 0x1FUL));
}

static void gic_eoi_irq(struct irq_data *d)
{
	u32 hwirq = d->hwirq;

	// printf("this is %s(): %d\r\n", __func__, __LINE__);
  	gic_data.gic->C_EOIR = hwirq;
}

static const struct irq_chip gic_chip = {
	.irq_mask		= gic_mask_irq,
	.irq_unmask		= gic_unmask_irq,
	.irq_eoi		= gic_eoi_irq,
};

int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
	// struct gic_chip_data *gic = d->host_data;
	// struct irq_data *irqd = irq_desc_get_irq_data(irq_to_desc(irq));

	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	switch (hw) {
	case 0 ... 31:
		// irq_set_percpu_devid(irq);
		// irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data,
		// 		    handle_percpu_devid_irq, NULL, NULL);
		break;
	default:
		// irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data, handle_fasteoi_irq, NULL, NULL);
		irq_domain_set_info(d, irq, hw, &gic_data.chip, NULL, handle_fasteoi_irq, NULL, NULL);
		// irq_set_probe(irq);
		// irqd_set_single_target(irqd);
		break;
	}

	/* Prevents SW retriggers which mess up the ACK/EOI ordering */
	// irqd_set_handle_enforce_irqctx(irqd);
	return 0;
}

static const struct irq_domain_ops gic_irq_domain_ops = {
	.map = gic_irq_domain_map,
	// .unmap = gic_irq_domain_unmap,
};

void gic_handle_irq(void) 
{
	// printf("this is %s(): %d\r\n", __func__, __LINE__);
	GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
	uint32_t hwirq = gic->C_IAR & 0x1FFFUL;
	// printf("this is %s(): %d  hwirq = %d\r\n", __func__, __LINE__, hwirq);

	if ((hwirq == 1023) || (hwirq >= NUMBER_OF_INT_VECTORS)) {
		printf("this is %s(): %d >>> interrupts out of around!\r\n", __func__, __LINE__);
		return;
	}
	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	handle_domain_irq(gic_data.domain, hwirq, NULL);
	// printf("this is %s(): %d\r\n", __func__, __LINE__);
}

void gic_init(void)
{
	// printf("this is %s(): %d\r\n", __func__, __LINE__);
	GIC_Init();

	gic_data.gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
	gic_data.chip = gic_chip;

	set_handle_irq(gic_handle_irq);

	gic_data.domain = irq_domain_create_linear(NR_IRQS, &gic_irq_domain_ops, NULL);

	irq_create_mapping_affinity(gic_data.domain, EPIT1_IRQn, NULL);
	irq_create_mapping_affinity(gic_data.domain, GPIO1_Combined_0_15_IRQn, NULL);
}
